/*
 * Bloque.cpp
 *
 *  Created on: 21/04/2013
 *      Author: pancho
 */

#include "Bloque.h"


Bloque::Bloque()
{
	this->ListaRegistros = new list<Registro*>();
}

//Se crea una nueva instancia de Bloque con el numero de bloque, tamanio de disperion y tamanio de bloque
Bloque::Bloque(unsigned short int nuevoNumeroBloque, unsigned short int nuevoTamanioDispersion, unsigned int nuevoTamanioBloque)
{
	this->setNumeroBloque(nuevoNumeroBloque);
	this->setTamanioDispersion(nuevoTamanioDispersion);
	this->setTamanioBloque(nuevoTamanioBloque);
	this->ListaRegistros = new list<Registro*>();
}

Bloque::Bloque(string tipo, char* datos) {
	this->ListaRegistros = new list<Registro*>();
	unsigned int posicion = 0;
	memcpy(&(this->NumeroBloque), &(datos[posicion]), sizeof(unsigned short int));
	posicion += sizeof(unsigned short int); //2
	memcpy(&(this->TamanioDispersion), &(datos[posicion]), sizeof(unsigned short int));
	posicion += sizeof(unsigned short int);//4
	memcpy(&(this->TamanioBloque), &(datos[posicion]), sizeof(unsigned int));
	posicion += sizeof(unsigned int);//8
	unsigned int tamanioDatos=0;
	memcpy(&tamanioDatos, &(datos[posicion]), sizeof(unsigned int));
	posicion += sizeof(unsigned int); //12
	if(tipo == "unsigned int") {
	while(posicion < tamanioDatos) {
			RegistroOffset* registro = new RegistroOffset(&(datos[posicion]));
			posicion += registro->getTamanioChar();
			this->InsertarRegistro(registro);
		}
	}

	else if(tipo == "char*") {
		while(posicion < tamanioDatos) {
			RegistroTitulo* registro = new RegistroTitulo(&(datos[posicion]));
			posicion += registro->getTamanioChar();
			this->InsertarRegistro(registro);
		}
	}
}

unsigned short int Bloque::getNumeroBloque()
{
	return this->NumeroBloque;
}

unsigned short int Bloque::getTamanioDispersion()
{
	return this->TamanioDispersion;
}

unsigned int Bloque::getTamanioBloque()
{
	return this->TamanioBloque;
}

list<Registro*> * Bloque::getListaRegistros()
{
	return this->ListaRegistros;
}

void Bloque::setNumeroBloque(unsigned short int nuevoNumeroBloque)
{
	this->NumeroBloque = nuevoNumeroBloque;
}

void Bloque::setTamanioDispersion(unsigned short int nuevoTamanioDispersion)
{
	this->TamanioDispersion = nuevoTamanioDispersion;
}

void Bloque::setTamanioBloque(unsigned int nuevoTamanioBloque)
{
	this->TamanioBloque = nuevoTamanioBloque;
}

void Bloque::setListaRegistros(list<Registro*>* nuevaListaRegistros)
{
	delete this->ListaRegistros;
	this->ListaRegistros = nuevaListaRegistros;
}

//Falta validar si se puede insertar o no, evaluando el espacio del bloque
bool Bloque::InsertarRegistro(Registro* registro)
{
	//Me fijo si ya hay alguno igual, si lo hay, chequeo si entra el id
	list<Registro*>::iterator iteradorLista = this->getListaRegistros()->begin();
	bool encontrado = false;

	// Buscamos el elemento mientras no lo encuentre o no llegue al final de la lista.
    while (!encontrado && iteradorLista != this->getListaRegistros()->end())
		{

		if ((*iteradorLista)->SosIgual(registro))
		{
			encontrado = true;
		}else
		{
			iteradorLista++;
		}
		}

    if ((encontrado) && chequearSiEntraElDato(registro->getId()->front()))
    {
    	(*iteradorLista)->agregarId(registro->getId()->front());
    	return true;
    }
    else if( !encontrado && chequearSiEntraElDato(registro))
    {
    	this->getListaRegistros()->push_back(registro);
    	return true;
    }

	return false;
}


bool Bloque::EstaRegistro(unsigned int idCancion)
{
	list<Registro*>::iterator iteradorLista = this->getListaRegistros()->begin();
	bool encontrado = false;

	// Buscamos el elemento mientras no lo encuentre o no llegue al final de la lista.
    while ( (!encontrado) && (iteradorLista != this->getListaRegistros()->end()) )
    	{
    	   try
    	   {
    	      RegistroOffset *unRegistroOffset = dynamic_cast<RegistroOffset*> (*iteradorLista);
    	      if(unRegistroOffset->getId()->front() == idCancion)
    	      {
    	    	  encontrado = true;
    	      }
    	   }
    	   catch (std::bad_cast) {
    	      cout << "Se ha ingresado a la tabla de hash un Registro que no pertenece a la clase RegistroOffset" << endl;
    	   }

    	iteradorLista++;
    	}

return encontrado;

}

//posCondicion: Si no se encuentran registros, se devuelve un puntero nulo.
Registro * Bloque::BuscarRegistro(unsigned int idCancion)
{

	list<Registro*>::iterator iteradorLista = this->getListaRegistros()->begin();

	// Buscamos el elemento mientras no lo encuentre o no llegue al final de la lista.
    while (iteradorLista != this->getListaRegistros()->end())
    	{
    	   try
    	   {
    	      RegistroOffset *unRegistroOffset = dynamic_cast<RegistroOffset*> (*iteradorLista);
    	      if(unRegistroOffset->getId()->front() == idCancion)
    	      {
    	    	  return unRegistroOffset;
    	      }
    	   }
    	   catch (std::bad_cast){
    	      cout << "Se ha ingresado a la tabla de hash un Registro que no pertenece a la clase RegistroOffset" << endl;
    	   }

    	iteradorLista++;
    	}

	return NULL;

}

bool Bloque::EstaRegistro(string unTitulo) {

	list<Registro*>::iterator iteradorLista = this->getListaRegistros()->begin();
	bool encontrado = false;
	// Buscamos el elemento mientras no lo encuentre o no llegue al final de la lista.
    while ((!encontrado) && (iteradorLista != this->getListaRegistros()->end()))
    	{
    	   try
    	   {
    	      RegistroTitulo *unRegistroTitulo = dynamic_cast<RegistroTitulo*> (*iteradorLista);
    	      if(unRegistroTitulo->getTitulo()== unTitulo)
    	      {
    	    	  encontrado = true;
    	      }
    	   }
    	   catch (bad_cast) {
    	      cout << "Se ha ingresado a la tabla de hash un Registro que no pertenece a la clase RegistroOffset" << endl;
    	   }

    	iteradorLista++;
    	}

	return encontrado;

}

//posCondicion: Si no se encuentran registros, se devuelve un puntero nulo.
Registro* Bloque::BuscarRegistro(string tituloABuscar)
{
	list<Registro*>::iterator iteradorLista = this->getListaRegistros()->begin();
	bool encontrado=false;
	// Buscamos el elemento mientras no llegue al final de la lista.
    while ((!encontrado) && (iteradorLista != this->getListaRegistros()->end()))
    	{
    	   try
    	   {
    	      RegistroTitulo *unRegistroTitulo = dynamic_cast<RegistroTitulo*> (*iteradorLista);
    	      if(unRegistroTitulo->getTitulo() == tituloABuscar)
    	      {
    	    	  return unRegistroTitulo;
    	      }
    	   }
    	   catch (std::bad_cast) {
    	      cout << "Se ha ingresado a la tabla de hash un Registro que no pertenece a la clase RegistroOffset" << endl;
    	   }

    	iteradorLista++;
    	}

	return NULL;

}

void Bloque::DuplicarTamanioDispersion()
{
	this->setTamanioDispersion(this->getTamanioDispersion()*2);
}

char* Bloque::blockToChar() {
	unsigned int posicion = 0;
	char* datosBloque = new char[this->TamanioBloque];
	memcpy(&(datosBloque[posicion]), (char*)&this->NumeroBloque, sizeof(unsigned short int));
	posicion += sizeof(unsigned short int);
	memcpy(&(datosBloque[posicion]), (char*)&this->TamanioDispersion, sizeof(unsigned short int));
	posicion += sizeof(unsigned short int);
	memcpy(&(datosBloque[posicion]), (char*)&this->TamanioBloque, sizeof(unsigned int));
	posicion += sizeof(unsigned int);
	posicion += sizeof(unsigned int); //dejo espacio para guardar el tamanio de los datos del bloque
	list<Registro*>::iterator iteradorLista = this->ListaRegistros->begin();
	while (iteradorLista != this->ListaRegistros->end()) {
		char* datosRegistro = (*iteradorLista)->regToChar();
		memcpy(&(datosBloque[posicion]), datosRegistro, (*iteradorLista)->getTamanioChar());
		delete datosRegistro;
		posicion += (*iteradorLista)->getTamanioChar();
		iteradorLista++;
	}
	unsigned int tamanio = posicion;
	memcpy(&(datosBloque[sizeof(unsigned short int)*2 + sizeof(unsigned int)]), (char*)&(tamanio), sizeof(unsigned int));

	return datosBloque;
}

Bloque::~Bloque() {

	list<Registro*>::iterator iteradorLista = this->getListaRegistros()->begin();

	while (iteradorLista != this->getListaRegistros()->end() )
		{
			Registro * unRegistroAEliminar = *iteradorLista;
			delete unRegistroAEliminar;
			iteradorLista++;
		}
	this->ListaRegistros->clear();
	delete this->ListaRegistros;
		}

		bool Bloque::chequearSiEntraElDato(string unTitulo) {
			bool entra = false;

			unsigned int posicion = sizeof(unsigned short int)*2 + sizeof(unsigned int)*2;
			list<Registro*>::iterator iteradorLista = this->ListaRegistros->begin();
			while (iteradorLista != this->ListaRegistros->end()) {
				posicion += (*iteradorLista)->getTamanioChar();
				iteradorLista++;
			}

			if(posicion + unTitulo.size() <= this->TamanioBloque) { //poner un porcentaje de carga
				entra = true;
			}
			return entra;
		}

		bool Bloque::chequearSiEntraElDato(unsigned int unId) {
			bool entra = false;

			unsigned int posicion = sizeof(unsigned short int)*2 + sizeof(unsigned int)*2;
			list<Registro*>::iterator iteradorLista = this->ListaRegistros->begin();
			while (iteradorLista != this->ListaRegistros->end()) {
				posicion += (*iteradorLista)->getTamanioChar();
				iteradorLista++;
			}

			if(posicion + sizeof(unsigned int) <= this->TamanioBloque) { //poner un porcentaje de carga
				entra = true;
			}
			return entra;
}

		bool Bloque::chequearSiEntraElDato(Registro *unRegistro) {
			bool entra = false;

			unsigned int posicion = sizeof(unsigned short int)*2 + sizeof(unsigned int)*2;
			list<Registro*>::iterator iteradorLista = this->ListaRegistros->begin();
			while (iteradorLista != this->ListaRegistros->end()) {
				posicion += (*iteradorLista)->getTamanioChar();
				iteradorLista++;
			}
			if(posicion + unRegistro->getTamanioChar() <= this->TamanioBloque) { //poner un porcentaje de carga
				entra = true;
			}
			return entra;
}

void Bloque::DividirTamanioDispersion() {
	this->setTamanioDispersion(this->getTamanioDispersion()/2);
}

void Bloque::eliminarId(string unTitulo, unsigned int id) {
	list<Registro*>::iterator iteradorLista = this->getListaRegistros()->begin();
	bool encontrado=false;

	if(this->EstaRegistro(unTitulo))
	{
	    while ((!encontrado) && (iteradorLista != this->getListaRegistros()->end()))
	    	{
	    	   try
	    	   {
	    	      RegistroTitulo *unRegistroTitulo = dynamic_cast<RegistroTitulo*> (*iteradorLista);
	    	      if(unRegistroTitulo->getTitulo() == unTitulo)
	    	      {
	    	    	  encontrado=true;;
	    	      }
	    	      else
	    	      {
	    	      	iteradorLista++;
	    	      }
	    	   }
	    	   catch (std::bad_cast) {
	    	      cout << "Se ha ingresado a la tabla de hash un Registro que no pertenece a la clase RegistroTitulo" << endl;
	    	   }
	    	}

	    if((*iteradorLista)->getId()->size() == 1 && (*iteradorLista)->getId()->front() == id)
		{
			this->getListaRegistros()->remove(*iteradorLista);
		}else
		{
			(*iteradorLista)->getId()->remove(id);
		}
	}

}

void Bloque::eliminarId(unsigned int idOffset) {
	list<Registro*>::iterator iteradorLista = this->getListaRegistros()->begin();
	bool encontrado=false;

	if(this->EstaRegistro(idOffset))
	{
	    while ((!encontrado) && (iteradorLista != this->getListaRegistros()->end()))
	    	{
	    	   try
	    	   {
	    	      RegistroOffset *unRegistroOffset = dynamic_cast<RegistroOffset*> (*iteradorLista);
	    	      if(unRegistroOffset->getId()->front() == idOffset)
	    	      {
	    	    	  this->getListaRegistros()->remove(*iteradorLista);
	    	    	  encontrado=true;
	    	      }
	    	      else
	    	      {
	    	      	iteradorLista++;
	    	      }
	    	   }
	    	   catch (std::bad_cast) {
	    	      cout << "Se ha ingresado a la tabla de hash un Registro que no pertenece a la clase RegistroOffset" << endl;
	    	   }
	    	}


	}

}
